"""
提示词渲染引擎
提供高级的提示词渲染和参数处理功能
"""
import re
import json
import yaml
from typing import Dict, List, Any, Optional, Union
from datetime import datetime
from jinja2 import Template, Environment, BaseLoader
from pydantic import BaseModel, Field

from models.prompt_template import ParameterType, OutputFormat
from core.logging_config import get_logger

logger = get_logger("prompt_renderer")


class RenderContext(BaseModel):
    """渲染上下文"""
    template_id: int
    template_name: str
    parameters: Dict[str, Any]
    user_id: Optional[int] = None
    session_id: Optional[str] = None
    metadata: Dict[str, Any] = Field(default_factory=dict)


class RenderResult(BaseModel):
    """渲染结果"""
    rendered_prompt: str
    parameters_used: Dict[str, Any]
    missing_parameters: List[str]
    validation_errors: List[str]
    render_time: float
    token_count: Optional[int] = None
    context: RenderContext


class PromptRenderer:
    """提示词渲染引擎"""
    
    def __init__(self):
        # 创建Jinja2环境
        self.jinja_env = Environment(loader=BaseLoader())
        # 注册自定义过滤器
        self._register_filters()
    
    def render(
        self,
        template_content: str,
        template_output_schema: str,
        parameters: Dict[str, Any],
        parameter_definitions: List[Dict[str, Any]] = None,
        context: Optional[RenderContext] = None
    ) -> RenderResult:
        """渲染提示词"""
        
        import time
        start_time = time.time()
        
        try:
            # 参数预处理
            processed_params = self._preprocess_parameters(parameters, parameter_definitions)
            
            # 验证参数
            validation_errors = self._validate_parameters(processed_params, parameter_definitions)
            missing_params = self._check_missing_parameters(template_content, processed_params)
            
            # 使用Jinja2渲染
            jinja_template = self.jinja_env.from_string(template_content)
            rendered_prompt = jinja_template.render(**processed_params)
            
            # 后处理
            rendered_prompt = self._postprocess_render(rendered_prompt)

            # 增加输出结果
            rendered_prompt += template_output_schema
            # 计算token数量
            token_count = self._estimate_tokens(rendered_prompt)
            
            render_time = time.time() - start_time
            
            return RenderResult(
                rendered_prompt=rendered_prompt,
                parameters_used=processed_params,
                missing_parameters=missing_params,
                validation_errors=validation_errors,
                render_time=render_time,
                token_count=token_count,
                context=context or RenderContext(template_id=0, template_name="", parameters=parameters)
            )
            
        except Exception as e:
            logger.error(f"提示词渲染失败: {str(e)}")
            raise
    
    def render_with_variables(
        self,
        template_content: str,
        variables: Dict[str, Any],
        parameter_definitions: List[Dict[str, Any]] = None
    ) -> RenderResult:
        """使用变量渲染提示词"""
        
        # 转换变量为参数格式
        parameters = {}
        for key, value in variables.items():
            parameters[key] = {
                'value': value,
                'type': self._infer_type(value)
            }
        
        return self.render(template_content, parameters, parameter_definitions)
    
    def render_batch(
        self,
        template_content: str,
        batch_parameters: List[Dict[str, Any]],
        parameter_definitions: List[Dict[str, Any]] = None
    ) -> List[RenderResult]:
        """批量渲染提示词"""
        
        results = []
        for i, parameters in enumerate(batch_parameters):
            context = RenderContext(
                template_id=0,
                template_name=f"batch_{i}",
                parameters=parameters
            )
            result = self.render(template_content, parameters, parameter_definitions, context)
            results.append(result)
        
        return results
    
    def validate_template(
        self,
        template_content: str,
        parameter_definitions: List[Dict[str, Any]] = None
    ) -> Dict[str, Any]:
        """验证模板语法"""
        
        errors = []
        warnings = []
        
        try:
            # 检查Jinja2语法
            self.jinja_env.from_string(template_content)
            
            # 检查参数占位符
            jinja_placeholders = self._extract_jinja_placeholders(template_content)
            simple_placeholders = self._extract_simple_placeholders(template_content)
            
            if parameter_definitions:
                param_names = {param['name'] for param in parameter_definitions}
                
                # 检查未使用的参数
                used_placeholders = set(jinja_placeholders + simple_placeholders)
                unused_params = param_names - used_placeholders
                if unused_params:
                    warnings.append(f"未使用的参数: {', '.join(unused_params)}")
                
                # 检查未定义的占位符
                undefined_placeholders = used_placeholders - param_names
                if undefined_placeholders:
                    errors.append(f"未定义的占位符: {', '.join(undefined_placeholders)}")
            
            # 检查模板复杂度
            complexity = self._analyze_complexity(template_content)
            if complexity['condition_count'] > 10:
                warnings.append(f"模板条件语句过多 ({complexity['condition_count']})")
            if complexity['loop_count'] > 5:
                warnings.append(f"模板循环语句过多 ({complexity['loop_count']})")
            
        except Exception as e:
            errors.append(f"模板语法错误: {str(e)}")
        
        return {
            'valid': len(errors) == 0,
            'errors': errors,
            'warnings': warnings,
            'complexity': complexity if 'complexity' in locals() else {}
        }
    
    def generate_example_usage(
        self,
        template_content: str,
        parameter_definitions: List[Dict[str, Any]] = None
    ) -> Dict[str, Any]:
        """生成示例用法"""
        
        examples = []
        
        if parameter_definitions:
            # 为每个参数生成示例值
            example_params = {}
            for param in parameter_definitions:
                example_value = self._generate_example_value(param)
                example_params[param['name']] = example_value
            
            try:
                result = self.render(template_content, example_params, parameter_definitions)
                examples.append({
                    'name': '完整示例',
                    'parameters': example_params,
                    'rendered_prompt': result.rendered_prompt
                })
            except Exception as e:
                logger.warning(f"生成示例失败: {str(e)}")
        
        # 生成最小示例
        minimal_params = {}
        if parameter_definitions:
            for param in parameter_definitions:
                if param.get('is_required', False):
                    minimal_params[param['name']] = self._generate_minimal_value(param)
        
        if minimal_params:
            try:
                result = self.render(template_content, minimal_params, parameter_definitions)
                examples.append({
                    'name': '最小示例',
                    'parameters': minimal_params,
                    'rendered_prompt': result.rendered_prompt
                })
            except Exception as e:
                logger.warning(f"生成最小示例失败: {str(e)}")
        
        return {
            'examples': examples,
            'parameter_count': len(parameter_definitions) if parameter_definitions else 0,
            'required_parameters': [p['name'] for p in parameter_definitions if p.get('is_required', False)] if parameter_definitions else []
        }
    
    def _preprocess_parameters(
        self,
        parameters: Dict[str, Any],
        parameter_definitions: List[Dict[str, Any]] = None
    ) -> Dict[str, Any]:
        """参数预处理"""
        
        processed = {}
        
        for key, value in parameters.items():
            # 如果是字典格式 {'value': ..., 'type': ...}，提取值
            if isinstance(value, dict) and 'value' in value:
                processed[key] = value['value']
            else:
                processed[key] = value
            
            # 应用默认值
            if parameter_definitions:
                for param_def in parameter_definitions:
                    if param_def['name'] == key and key not in processed:
                        if 'default_value' in param_def:
                            processed[key] = param_def['default_value']
        
        return processed
    
    def _validate_parameters(
        self,
        parameters: Dict[str, Any],
        parameter_definitions: List[Dict[str, Any]] = None
    ) -> List[str]:
        """验证参数"""
        
        errors = []
        
        if not parameter_definitions:
            return errors
        
        for param_def in parameter_definitions:
            param_name = param_def['name']
            param_value = parameters.get(param_name)
            
            # 检查必填参数
            if param_def.get('is_required', False) and param_value is None:
                errors.append(f"参数 '{param_name}' 是必填的")
                continue
            
            if param_value is not None:
                # 类型验证
                expected_type = param_def.get('param_type', ParameterType.STRING)
                if not self._validate_type(param_value, expected_type):
                    errors.append(f"参数 '{param_name}' 类型错误，期望 {expected_type}")
                
                # 长度验证
                if 'min_length' in param_def and len(str(param_value)) < param_def['min_length']:
                    errors.append(f"参数 '{param_name}' 长度不足，最小长度 {param_def['min_length']}")
                
                if 'max_length' in param_def and len(str(param_value)) > param_def['max_length']:
                    errors.append(f"参数 '{param_name}' 长度超出，最大长度 {param_def['max_length']}")
                
                # 数值范围验证
                if 'min_value' in param_def and isinstance(param_value, (int, float)):
                    if param_value < param_def['min_value']:
                        errors.append(f"参数 '{param_name}' 值过小，最小值 {param_def['min_value']}")
                
                if 'max_value' in param_def and isinstance(param_value, (int, float)):
                    if param_value > param_def['max_value']:
                        errors.append(f"参数 '{param_name}' 值过大，最大值 {param_def['max_value']}")
                
                # 枚举值验证
                if 'enum_values' in param_def and param_def['enum_values']:
                    if param_value not in param_def['enum_values']:
                        errors.append(f"参数 '{param_name}' 值无效，有效值: {param_def['enum_values']}")
                
                # 正则表达式验证
                if 'pattern' in param_def and param_def['pattern']:
                    import re
                    if not re.match(param_def['pattern'], str(param_value)):
                        errors.append(f"参数 '{param_name}' 格式不匹配模式: {param_def['pattern']}")
        
        return errors
    
    def _check_missing_parameters(
        self,
        template_content: str,
        parameters: Dict[str, Any]
    ) -> List[str]:
        """检查缺失的参数"""
        
        # 提取模板中的占位符
        jinja_placeholders = self._extract_jinja_placeholders(template_content)
        simple_placeholders = self._extract_simple_placeholders(template_content)
        
        all_placeholders = set(jinja_placeholders + simple_placeholders)
        
        # 找出缺失的参数
        missing = []
        for placeholder in all_placeholders:
            if placeholder not in parameters:
                missing.append(placeholder)
        
        return missing
    
    def _extract_jinja_placeholders(self, content: str) -> List[str]:
        """提取Jinja2占位符"""
        # 提取变量 {{ variable }}
        vars_pattern = r'\{\{\s*([^}\s]+)\s*\}\}'
        variables = re.findall(vars_pattern, content)
        
        # 提取循环和条件中的变量
        loop_pattern = r'\{%\s*(?:for|if)\s+([^}\s]+)'
        loop_vars = re.findall(loop_pattern, content)
        
        return list(set(variables + loop_vars))
    
    def _extract_simple_placeholders(self, content: str) -> List[str]:
        """提取简单占位符 {parameter}"""
        simple_pattern = r'\{([^}\s]+)\}'
        placeholders = re.findall(simple_pattern, content)
        
        # 过滤掉Jinja2相关的占位符
        filtered = [p for p in placeholders if not p.startswith('%') and not p.startswith('{')]
        
        return filtered
    
    def _validate_type(self, value: Any, expected_type: str) -> bool:
        """验证参数类型"""
        try:
            if expected_type == ParameterType.STRING:
                return isinstance(value, str)
            elif expected_type == ParameterType.INTEGER:
                return isinstance(value, int)
            elif expected_type == ParameterType.FLOAT:
                return isinstance(value, (int, float))
            elif expected_type == ParameterType.BOOLEAN:
                return isinstance(value, bool)
            elif expected_type == ParameterType.ARRAY:
                return isinstance(value, list)
            elif expected_type == ParameterType.OBJECT:
                return isinstance(value, dict)
            elif expected_type == ParameterType.DATE:
                from datetime import date
                return isinstance(value, (date, str))
            elif expected_type == ParameterType.DATETIME:
                from datetime import datetime
                return isinstance(value, (datetime, str))
            return True
        except:
            return False
    
    def _infer_type(self, value: Any) -> str:
        """推断参数类型"""
        if isinstance(value, bool):
            return ParameterType.BOOLEAN
        elif isinstance(value, int):
            return ParameterType.INTEGER
        elif isinstance(value, float):
            return ParameterType.FLOAT
        elif isinstance(value, list):
            return ParameterType.ARRAY
        elif isinstance(value, dict):
            return ParameterType.OBJECT
        else:
            return ParameterType.STRING
    
    def _generate_example_value(self, param_def: Dict[str, Any]) -> Any:
        """生成示例值"""
        param_type = param_def.get('param_type', ParameterType.STRING)
        
        if 'enum_values' in param_def and param_def['enum_values']:
            return param_def['enum_values'][0]
        
        if 'default_value' in param_def and param_def['default_value']:
            return param_def['default_value']
        
        if param_type == ParameterType.STRING:
            return f"示例_{param_def['name']}"
        elif param_type == ParameterType.INTEGER:
            return 42
        elif param_type == ParameterType.FLOAT:
            return 3.14
        elif param_type == ParameterType.BOOLEAN:
            return True
        elif param_type == ParameterType.ARRAY:
            return ["项1", "项2", "项3"]
        elif param_type == ParameterType.OBJECT:
            return {"key": "value"}
        elif param_type == ParameterType.DATE:
            return "2024-01-01"
        elif param_type == ParameterType.DATETIME:
            return "2024-01-01T00:00:00"
        else:
            return "示例值"
    
    def _generate_minimal_value(self, param_def: Dict[str, Any]) -> Any:
        """生成最小值"""
        param_type = param_def.get('param_type', ParameterType.STRING)
        
        if param_type == ParameterType.STRING:
            return ""
        elif param_type == ParameterType.INTEGER:
            return 0
        elif param_type == ParameterType.FLOAT:
            return 0.0
        elif param_type == ParameterType.BOOLEAN:
            return False
        elif param_type == ParameterType.ARRAY:
            return []
        elif param_type == ParameterType.OBJECT:
            return {}
        elif param_type == ParameterType.DATE:
            return "2024-01-01"
        elif param_type == ParameterType.DATETIME:
            return "2024-01-01T00:00:00"
        else:
            return ""
    
    def _postprocess_render(self, content: str) -> str:
        """后处理渲染结果"""
        # 清理多余的空行
        lines = [line.rstrip() for line in content.split('\n')]
        # 移除连续的空行
        result = []
        prev_empty = False
        for line in lines:
            if line.strip() == '':
                if not prev_empty:
                    result.append('')
                prev_empty = True
            else:
                result.append(line)
                prev_empty = False
        
        return '\n'.join(result)
    
    def _estimate_tokens(self, text: str) -> int:
        """估算token数量"""
        # 简单的token估算：中文字符算2个token，英文单词算1个token
        import re
        
        # 统计中文字符
        chinese_chars = len(re.findall(r'[\u4e00-\u9fff]', text))
        
        # 统计英文单词
        english_words = len(re.findall(r'\b[a-zA-Z]+\b', text))
        
        # 其他字符
        other_chars = len(text) - chinese_chars - len(re.findall(r'[a-zA-Z\s]', text))
        
        return chinese_chars * 2 + english_words + other_chars // 4
    
    def _analyze_complexity(self, content: str) -> Dict[str, int]:
        """分析模板复杂度"""
        
        # 统计条件语句
        condition_count = len(re.findall(r'\{%\s*if\s', content))
        
        # 统计循环语句
        loop_count = len(re.findall(r'\{%\s*for\s', content))
        
        # 统计变量引用
        variable_count = len(re.findall(r'\{\{[^}]+\}\}', content))
        
        # 统计模板包含
        include_count = len(re.findall(r'\{%\s*include\s', content))
        
        return {
            'condition_count': condition_count,
            'loop_count': loop_count,
            'variable_count': variable_count,
            'include_count': include_count,
            'total_score': condition_count * 2 + loop_count * 3 + variable_count + include_count * 2
        }
    
    def _register_filters(self):
        """注册自定义过滤器"""
        
        def to_json_filter(value):
            """转换为JSON格式"""
            import json
            return json.dumps(value, ensure_ascii=False, indent=2)

        def to_yaml_filter(value):
            """转换为YAML格式"""
            import yaml
            return yaml.dump(value, allow_unicode=True, default_flow_style=False)
        
        def truncate_words_filter(text, length=50):
            """按单词截断文本"""
            if len(text) <= length:
                return text
            return text[:length] + "..."
        
        def format_date_filter(date_value, format_str="%Y-%m-%d"):
            """格式化日期"""
            if isinstance(date_value, str):
                try:
                    from datetime import datetime
                    date_value = datetime.fromisoformat(date_value.replace('Z', '+00:00'))
                except:
                    return date_value
            
            if hasattr(date_value, 'strftime'):
                return date_value.strftime(format_str)
            
            return str(date_value)
        
        def escape_prompt_filter(text):
            """转义提示词中的特殊字符"""
            return text.replace('{', '{{').replace('}', '}}')
        
        # 注册过滤器
        self.jinja_env.filters['to_json'] = to_json_filter
        self.jinja_env.filters['to_yaml'] = to_yaml_filter
        self.jinja_env.filters['truncate_words'] = truncate_words_filter
        self.jinja_env.filters['format_date'] = format_date_filter
        self.jinja_env.filters['escape_prompt'] = escape_prompt_filter


# 全局渲染引擎实例
prompt_renderer = PromptRenderer()